﻿using System;
using System.Data.Common;
using System.IO;
using System.Linq;
using System.Security;
using System.Xml;

using Centido.Core;

namespace Centido.Management.ConsoleManager
{
    /// <summary>
    ///   A console command that creates a backup of the current domain.
    /// </summary>
    internal class BackupCommand : Command
    {
        /// <summary>
        ///   The file name parameter.
        /// </summary>
        public const string FileNameParameter = "file";

        /// <summary>
        ///   The file name parameter (short name).
        /// </summary>
        public const string FileNameShortParameter = "f";


        /// <summary>
        ///   The included in the baclup domain components parameter.
        /// </summary>
        public const string IncludedComponentsParameter = "include";


        /// <summary>
        ///   The included in the baclup domain components parameter (short name).
        /// </summary>
        public const string IncludedComponentsShortParameter = "i";


        /// <summary>
        ///   Gets the console command name.
        /// </summary>
        public override string Name
        {
            get { return "backup"; }
        }


        /// <summary>
        ///   Gets the console command description.
        /// </summary>
        public override string Description
        {
            get { return Messages.BackupCommandDescription; }
        }


        /// <summary>
        ///   Gets the console command full description.
        /// </summary>
        public override string FullDescription
        {
            get { return Messages.BackupCommandFullDescription; }
        }


        /// <summary>
        ///   Reads the domain components parameter from the execution context.
        /// </summary>
        /// <param name="context">The command execution context.</param>
        /// <returns>Returns the value of domain components parameter or null.</returns>
        private DomainComponents? ReadDomainComponentsParameter(CommandExecutionContext context)
        {
            var parameter = context.GetParameterByName(IncludedComponentsParameter,
                IncludedComponentsShortParameter);

            if (parameter == null)
                return null;

            var values = parameter.Values.Select(v => v.ToLower()).ToList();

            var result = DomainComponents.None;

            if (values.Contains("all"))
                result |= DomainComponents.All;

            if (values.Contains("data"))
                result |= DomainComponents.Data;

            if (values.Contains("deletedrows"))
                result |= DomainComponents.DeletedRows;

            if (values.Contains("domainoptions"))
                result |= DomainComponents.DomainOptions;

            if (values.Contains("metadata"))
                result |= DomainComponents.Metadata;

            if (values.Contains("roles"))
                result |= DomainComponents.Roles;

            if (values.Contains("rows"))
                result |= DomainComponents.Rows;

            if (values.Contains("schema"))
                result |= DomainComponents.Schema;

            if (values.Contains("security"))
                result |= DomainComponents.Security;

            if (values.Contains("systemcolumns"))
                result |= DomainComponents.SystemColumns;

            if (values.Contains("permissions"))
                result |= DomainComponents.TablePermissions;

            if (values.Contains("units"))
                result |= DomainComponents.Units;

            if (values.Contains("users"))
                result |= DomainComponents.Users;

            if (result == DomainComponents.None)
                return null;

            return result;
        }


        /// <summary>
        ///   Executes the console command.
        /// </summary>
        /// <param name="context">The console command execution context.</param>
        /// <exception cref="ArgumentNullException">Thrown when the value of <paramref name="context"/> is null.</exception>
        public override void Execute(CommandExecutionContext context)
        {
            base.Execute(context);

            if (context.CurrentDomain == null) return;

            var domain = context.CurrentDomain;

            // If there is no permission to perform this operation
            if (!domain.CurrentUser.CanChangeSecurity
                || !domain.CurrentUser.CanChangeSchema)
            {
                Console.WriteLine(Messages.NeedCanChangeSecurityAndSchemaPermissionMessage);
                return;
            }

            var backupTask = new BackupTask(domain);

            // If the file name parameter was specified
            if (context.ParameterValueIsNotEmpty(FileNameParameter, FileNameShortParameter))
            {
                // The file name
                var fileName = context.GetParameterValue(FileNameParameter, FileNameShortParameter);

                backupTask.FileName = fileName;
            }

            // If the included domain components parameter was specified
            if (context.ParameterValueIsNotEmpty(IncludedComponentsParameter, IncludedComponentsShortParameter))
            {
                var domainComponents = ReadDomainComponentsParameter(context);

                // If the wrong value was specified
                if (domainComponents == null)
                {
                    Console.WriteLine(Messages.DomainComponentsWrongValueError);
                    return;
                }

                backupTask.IncludedComponents = domainComponents.Value;
            }

            // Displaying diagnostic messages
            backupTask.MessageReceived += (o, e) =>
                Console.WriteLine(e.Message);

            try
            {
                backupTask.Run();
            }
            catch (ArgumentException e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine();
                return;
            }
            catch (XmlException e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine();
                return;
            }
            catch (IOException e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine();
                return;
            }
            catch (DbException e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine();
                return;
            }
            catch (SecurityException e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine();
                return;
            }

            // Displaying the success message
            Console.WriteLine(Messages.CommandCompletedMessage);
        }
    }
}
